闭包

Q:什么是闭包?

  • 引用了自由变量的函数
  • 这个被引用的自由变量将和这个函数一同存在,即使是已经离开了创造它的环境也不例外。
  • 闭包是由函数和与其相关的引用环境组合而成的实体

Q:闭包的两个要素?

  • 返回一个内部函数
  • 被这个函数引用的外部变量

Q:举个例子?

1
2
3
4
5
6
7
8
9
10
11
12
def count():
i = 2
def f(j):
return j**i
return f

f1 = count()
print (f1.__closure__)
print(f1(3))
================= RESTART: closure.py =================
(<cell at 0x00000247059683A8: int object at 0x00007FF8274CB370>,)
9
1
2
3
4
5
6
7
8
9
10
11
12
def count():
i = 2
def f(j,i=i): # i不再是外部变量
return j**i
return f

f1 = count()
print (f1.__closure__)
print(f1(3))
================= RESTART: closure.py =================
None # 不再是闭包
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def count():
fs = []
for i in range(1,4):
def f():
return i*i
fs.append(f)
return fs

funcList = count()
for func in funcList:
print(func())
================= RESTART: closure.py =================
9
9
9

f1,f2,f3 = [lambda :i*i for i in range(1,4)]
print(f1(),f2(),f3())
print(i) # NameError: name 'i' is not defined
1
2
3
4
5
6
7
8
9
10
11
12
13
def makeadder(addend):
def adder(augend):
return augend + addend
return adder

add23 = makeadder(23)
add44 = makeadder(44)

print add23(100)
print add44(56)
=================== RESTART: 装饰器.py ===================
123
100

闭包的应用–装饰器

装饰器Decorator:以函数为参数返回一个可调用对象(含有__call__方法),实现其额外功能更新

应用场景:

  • 对原函数功能的补充:测量时间,输出日志等
  • 对原函数功能的调整:利用原函数运行结果,再次运算产生新结果
  • 对原函数功能的重写,只是借用原来的名字。
  • web路由
  • 身份认证

每个装饰器函数需要返回一个函数引用,因此要在函数内部再定义一个函数,一般都用wrapper这个名字

在装饰器函数内部进行功能扩展

  • 语法糖Syntactic Sugar:方便使用但无实质功能的语法

    采用@作为语法糖,辅助装饰器使用

    任何语法糖表达的装饰器都对应一个函数

    classmethod()内置函数和@classmethod

    staticmethod()和@staticmethod

1
2
3
4
5
6
7
def f(...):
...
f = classmethod(f)
# 等价于:
@staticmethod
def f(...):
...
1
2
3
4
5
6
7
@f1(arg)
@f2
def func():
pass

# 等价于
f1(arg)(f2(func))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def bar( foo ):
def wrapper( a ):
print("{:*^20}".format('BEGIN'))
foo( a )
print("{:*^20}".format('END'))
return wrapper


@bar
def printA( a ):
print("这是变量{}".format( a ))


printA("Python123")

'''未经装饰的输出
这是变量Python123
'''

'''装饰后的
*******BEGIN********
这是变量Python123
********END*********
'''
1
2
3
4
5
6
7
8
9
10
11
def add100(f):
def adder(n):
return f(n)+100
return adder

@add100
def num(n):
n = int(n)
return n

print(num(5))
1
2
3
4
5
6
7
8
9
10
11
def addn(n): # 接收 装饰器 参数
def makeadder(f): # 接收 被装饰 函数
def adder(x): # 接收 函数 参数
return f(x) + n
return adder
return makeadder

@addn(200)
def f(x):
return x
print(f(55))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def makebold(fn):
def wrapped():
return '<b>' +fn() +'</b>'
return wrapped

def makeitalic(fn):
def wrapped():
return '<i>' + fn() + '</i>'
return wrapped

@makebold
@makeitalic
def hello():
return 'hello world'

print(hello())
=================== RESTART:装饰器.py ===================
<b><i>hello world</i></b>
1
2
3
4
5
6
7
8
9
class function_wrapper(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)

@function_wrapper ##-> f = function_wrapper(f)
def f():
pass
1
2
3
4
5
6
7
8
9
10
from decorator import decorator

@decorator
def warpper(f, x):
return x**2
@warpper
def f(x):
return x

print f(5) # 25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import functools
def log(func):
#@functools.wraps(func)
def wrapper(*args, **kw):
print '%s() was called...' % func.__name__
return func(*args, **kw)
return wrapper

@log
def foo():
print('hello')

foo()
print(foo.__name__)